본문으로 건너뛰기

Generator와 async-await

🧑🏻‍💻 Generator


  • yeild 키워드와 next 메서드를 통해 코드 블록의 실행을 일시 중지했다가 필요한 시점에 재개할 수 있는 특수한 함수다.

  • function* 키워드로 선언하며 하나 이상의 yield 표현식을 포함한다.

  • 화살표 함수나 생성자 함수로 선언 및 호출할 수 없다.

//제너레이터 함수 선언문
function* genDecFunc() {
yield 1;
}

//제너레이터 함수 표현식
const genExpFunc = function* () {
yield 1;
};

//제너레이터 메서드
const obj = {
* genObiMethod( ) {
yield 1;
}
};

//제너레이터 클래스 메서드
class MyClass {
* genClsMethod() {
yield 1;
}
}

✅ Generator와 일반 함수의 차이

  1. non-blocking 가능 여부
    • 일반 함수 → 함수 호출자가 함수를 호출한 이후 함수 실행을 제어할 수 없다.
    • Generator 함수 → 함수 호출자에게 함수 실행의 제어권을 양도할 수 있다.
  1. 함수의 상태 전달
    • 일반 함수 → 함수가 실행되고 있는 동안에는 함수 외부에서 함수 내부르 값을 전달하여 함수의 상태를 변경할 수 없다.
    • Generator 함수 → 함수 호출자와 함수의 상태를 양방향으로 주고받을 수 있다.
  1. 반환 값
    • 일반 함수 → 호출하면 함수 코드를 일괄 실행하고 값을 반환한다.
    • Generator → 호출하면 이터러블이면서 이터레이터인 Generator 객체를 반환한다.

✅ Generator의 반환값 Generator 객체

  • Generator 객체는 Symbol.iterator 메서드를 상속받는 이터러블이면서 value, done 프로퍼티와 next 메서드를 가지는 이터레이터다.

  • 여기에 추가로 return, throw 메서드를 갖는다.

🧑🏻‍💻 async/await


✅ 사용 효과

const fetch = require("node-fetch");

async function fetchTodo() {
try {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
const response = await fetch(url);
const todo = await response.json();
} catch (e) {
console.log(e);
}
}
  • ES8에 나온 async/await를 사용하면, 제너레이터보다 간단하고 가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 구현 할 수 있다.

  • async/await 는 프로미스를 기반으로 동작하지만 프로미스의 then/catch/finally 와 같은 후속 메서드 없이도 동기 처리처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있다. (물론 사용은 할 수 있다.)

  • async/await 에서는 비동기 함수를 명시적으로 호출할 수 있기 때문에 호출자가 명확하다. 이 덕분에 try … catch 문을 사용하여 try 블록 내 모든 에러를 한번에 캐치할 수 있다.

✅ async 함수

  • async 함수는 언제나 프로미스를 반환한다.

  • 클래스의 constructor 메서드와 async 메서드는 각각 인스턴스와 프로미스를 항상 반환해야 하므로, constructor 메서드는 async 메서드가 될 수 없다.

✅ await 키워드

  • await 키워드는 반드시 async 함수 내부에서 사용해야 한다.

  • await 키워드는 반드시 Promise 를 반환하는 함수 앞 혹은 Promise 앞에서 사용해야 한다.

  • await 키워드를 남발하지 말자.

async function foo() {
const res = await Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000)),
new Promise(resolve => setTimeout(() => resolve(2), 2000)),
new Promise(resolve => setTimeout(() => resolve(3), 1000))
]);

console. log(res); // [1, 2, 3]
}
foo(); // 약 3초 소요된다.
  • foo 함수가 수행하는 3개의 비동기 처리는 서로 연관이 없이 개별적으로 수행되는 비동기 처리이다. 하지만 결과 값은 순서대로 출력된다.
async function foo() {
const a = await new Promise(resolve => setTimeout(() => resolve(1), 3000));
const b = await new Promise(resolve => setTimeout (() => resolve (2), 2000));
const c = await new Promise (resolve =setTimeout(() => resolve(3), 1000));
console. log([a, b, c]); // [1, 2, 3]
}
foo(); // 약 6초 소요된다.
  • 각 동작에 await를 추가하면 결과는 똑같지만 더 오래 걸린다.

참고 자료

  • 모던 자바스크립트 Deep Dive